﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Scripting.Utils;
using Scala.Compiler.Binding;
using System.Reflection;
using Scala.Compiler.Types;
using System.Reflection.Emit;

namespace Scala.Compiler.Ast
{
    public class NewStatement : Expression
    {
        private readonly Expression _typeName;
        private readonly IList<Arg> _args;

        public NewStatement(Expression typeName, IList<Arg> args)
        {
            ContractUtils.RequiresNotNull(typeName, "typeName");

            _typeName = typeName;
            _args = args;
        }

        public string InitType
        {
            get
            {
                if (_typeName is NameExpression)
                {
                    return (_typeName as NameExpression).Name;
                }
                if (_typeName is SelectStatement)
                {
                    var t = _typeName as SelectStatement;
                    if (t.ExprName is NameExpression)
                    {
                        return (t.ExprName as NameExpression).Name;
                    }
                }
                throw new Exception("Couldn't find the Type used in the 'new' Statement");
            }
        }

        public IList<Arg> Args { get { return _args; } }

        public ScalaType TargetScalaType { get; set; }

        public override void Walk(ScalaWalker walker)
        {
            if (walker.Walk(this))
            {
                if (_typeName != null)
                {
                    _typeName.Walk(walker);
                }
                if (_args != null)
                {
                    foreach (Arg arg in _args)
                    {
                        arg.Walk(walker);
                    }
                }
            }
            walker.PostWalk(this);
        }

        public override Microsoft.Scripting.Ast.Expression Reduce()
        {
            IList<Microsoft.Scripting.Ast.Expression> args = new List<Microsoft.Scripting.Ast.Expression>();
            IList<Type> paramTypes = new List<Type>();

            _typeName.Reduce();
            if (_typeName is SelectStatement)
            {
                TargetScalaType = (_typeName as SelectStatement).ScalaType;
            }
            if (_typeName is NameExpression)
            {
                TargetScalaType = (_typeName as NameExpression).ScalaType;
            }

            foreach (var arg in Args)
            {
                args.Add(arg.Reduce());
                Type t;
                if (!InternalTypes.GetType(arg.TypeName, Scope, out t)) throw new Exception(String.Format("Couldn't Resolve the Type {0} of the argument {1} in the function call in the Scope {2}", arg.TypeName, arg.Name, Scope.Name));
                paramTypes.Add(t);
            }

            Type targetType = TargetScalaType.TypeBuilder.AsType();
            ConstructorInfo c = TargetScalaType.GetConstructor(paramTypes);

            return Expression.NewStaticInit(c, args, paramTypes);
        }
    }
}
